package com.nvwa.coding.model;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonValue;
import lombok.Getter;

import java.time.LocalDate;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.concurrent.atomic.AtomicReference;

import static com.nvwa.coding.util.safe.Checker.isBlank;
import static com.nvwa.coding.util.safe.Safer.length;
import static com.nvwa.coding.util.safe.Safer.loggingGetOrNull;

public class StringTime implements ValueValidator {

    public static final DateTimeFormatter dtf_HHmm = DateTimeFormatter.ofPattern("HHmm"); // 4

    public static final DateTimeFormatter dtf_HH_mm = DateTimeFormatter.ofPattern("HH:mm"); // 仅输出时使用
    public static final DateTimeFormatter dtf_H_m = DateTimeFormatter.ofPattern("H:m"); // 3|4|5
    public static final DateTimeFormatter dtf_HHmmss = DateTimeFormatter.ofPattern("HHmmss"); // 6
    public static final DateTimeFormatter dtf_HH_mm_ss = DateTimeFormatter.ofPattern("HH:mm:ss"); // 仅输出时使用
    public static final DateTimeFormatter dtf_H_m_s = DateTimeFormatter.ofPattern("H:m:s"); // 5|6|7|8


    @Getter
    protected final String origin;

    @JsonIgnore
    protected final DateTimeFormatter originFormatter;

    @JsonIgnore
    protected AtomicReference<LocalTime> cache = new AtomicReference<>();

    @JsonCreator
    public StringTime(String origin) {
        this.origin = origin;
        this.originFormatter = parseTimeFormatter(origin);
    }

    public StringTime(String origin, DateTimeFormatter originFormatter) {
        this.origin = origin;
        this.originFormatter = originFormatter;
    }

    public StringTime(LocalTime localTime, DateTimeFormatter dateTimeFormatter) {
        this.origin = format(localTime, dateTimeFormatter);
        this.originFormatter = dateTimeFormatter;
        this.cache.set(localTime);
    }

    protected DateTimeFormatter parseTimeFormatter(String s) {
        switch (length(s)) {
            case 3:
                return dtf_H_m;
            case 4:
                return s.contains(":") ? dtf_H_m : dtf_HHmm;
            case 5:
                return s.replaceAll(":", "").length() == (5 - 2) ? dtf_H_m_s : dtf_H_m;
            case 6:
                return s.contains(":") ? dtf_H_m_s : dtf_HHmmss;
            case 7:
            case 8:
                return dtf_H_m_s;
            default:
                return null;
        }
    }

    public LocalTime asLocalTime() {
        if (isBlank(origin) || originFormatter == null || cache == null) {
            return null;
        }
        if (cache.get() != null) {
            return cache.get();
        }
        LocalTime localTime = loggingGetOrNull(() -> parse(origin, originFormatter));
        if (localTime == null) {
            cache = null;
            return null;
        }
        cache.set(localTime);
        return localTime;
    }


    /**
     * 定义解析字符串的规则
     *
     * @param s   字符串
     * @param dtf 使用的解析格式
     * @return 解析后的时间对象
     */
    protected LocalTime parse(String s, DateTimeFormatter dtf) {
        if (isBlank(s)) {
            return null;
        }
        return LocalTime.parse(s, dtf);
    }

    /**
     * 定义序列化规则
     *
     * @param localTime 时间对象
     * @param dtf       使用的序列化格式
     * @return 字符串格式的时间
     */
    protected String format(LocalTime localTime, DateTimeFormatter dtf) {
        return localTime.format(dtf);
    }

    public StringTime withDtf(DateTimeFormatter dtf) {
        if (originFormatter == dtf) {
            return this;
        }
        LocalTime localTime = asLocalTime();
        if (localTime != null) {
            return new StringTime(localTime, dtf);
        }
        return new StringTime(origin, dtf);
    }

    public static StringTime now_HHmm() {
        return StringTime.of_HHmm(LocalTime.now());
    }

    public static StringTime now_HH_mm() {
        return StringTime.of_HH_mm(LocalTime.now());
    }

    public static StringTime now_HHmmss() {
        return StringTime.of_HHmmss(LocalTime.now());
    }

    public static StringTime now_HH_mm_ss() {
        return StringTime.of_HH_mm_ss(LocalTime.now());
    }

    public static StringTime of_HHmm(LocalTime localTime) {
        return new StringTime(localTime, dtf_HHmm);
    }

    public static StringTime of_HH_mm(LocalTime localTime) {
        return new StringTime(localTime, dtf_HH_mm);
    }

    public static StringTime of_HHmmss(LocalTime localTime) {
        return new StringTime(localTime, dtf_HHmmss);
    }

    public static StringTime of_HH_mm_ss(LocalTime localTime) {
        return new StringTime(localTime, dtf_HH_mm_ss);
    }



    public StringTime to_HHmm() {
        return withDtf(dtf_HHmm);
    }

    public StringTime to_HH_mm() {
        return withDtf(dtf_HH_mm);
    }

    public StringTime to_HHmmss() {
        return withDtf(dtf_HHmmss);
    }

    public StringTime to_HH_mm_ss() {
        return withDtf(dtf_HH_mm_ss);
    }


    @Override
    public String toString() {
        return origin;
    }

    @JsonValue
    public String asString() {
        return origin;
    }

    @Override
    public boolean valid() {
        return asLocalTime() != null;
    }

    public StringDateTime withDate(LocalDate localDate, DateTimeFormatter dateTimeFormatter) {
        LocalTime localTime = asLocalTime();
        if (localTime != null) {
            return new StringDateTime(localTime.atDate(localDate), dateTimeFormatter);
        }
        return new StringDateTime(null);
    }

    public StringDateTime withDate(LocalDate localDate) {
        return withDate(localDate, StringDateTime.dtf_yyyyMMddHHmmss_A);
    }

    public StringDateTime withNow(DateTimeFormatter dateTimeFormatter) {
        return withDate(LocalDate.now(), dateTimeFormatter);
    }

    public StringDateTime withNow() {
        return withDate(LocalDate.now());
    }



}
